#ifndef _COMPILATION_SCOPE_TASK_H_
#define _COMPILATION_SCOPE_TASK_H_

#include <stack>

#include <compilation/task/Task.h>
#include <compilation/task/Handler.h>
#include <compilation/task/Propagator.h>


// ScopeTask and ScopeAgenda.
namespace compilation
{

class ScopeTask
    : public VirtualTask
{
public:

  explicit ScopeTask(VirtualInstruction& instruction)
  { setInstruction(instruction); }

  ScopeTask(ScopeTask const&) = default;

  virtual VirtualTask* clone(void) const
  { return new ScopeTask(*this); }

  void pushScope(Scope const& scope, int registrationIndex)
  { m_scope_stack.push(std::make_pair(&scope, registrationIndex)); }

  Scope const& getHeadScope(void) const
  { return *m_scope_stack.top().first; }

  int getHeadRegistrationIndex(void) const
  { return m_scope_stack.top().second; }

  void popScope(void)
  { m_scope_stack.pop(); }

private:

  std::stack<std::pair<Scope const*, int>> m_scope_stack;
};

class ScopeAgenda
    : public VirtualAgenda
{
private:

  std::list<VirtualInstruction*> m_markedInstruction;

public:

  typedef ScopeTask Task;

  typedef InstructionVisitor<
  void, ScopeTask&, ScopeAgenda&, WorkListReusability&>
  instruction_handler_visitor_type;

  typedef InstructionVisitor<
  bool, ScopeTask&, ScopeAgenda&, WorkListReusability&>
  instruction_propagator_visitor_type;

  instruction_handler_visitor_type const&
  handleInstruction;

  instruction_propagator_visitor_type const&
  propagateOnUnmarked;

public:

  ScopeAgenda(ScopeAgenda const& origin) = delete;

  ScopeAgenda(
      instruction_handler_visitor_type const& instruction_handler_visitor,
      instruction_propagator_visitor_type const& instruction_propagator_visitor,
      Function const& function)
    : handleInstruction(instruction_handler_visitor)
    , propagateOnUnmarked(instruction_propagator_visitor)
  { addNewAsFirst(new ScopeTask(function.getFirstInstruction())); }

  virtual ~ScopeAgenda(void);

  void markInstructionWith(
      VirtualInstruction &instruction, VirtualTask &task);
};

} // compilation

// Handler.
namespace compilation
{

template <InstructionType _Type>
struct ScopeInstructionHandler
    : public InstructionHandler<_Type, ScopeTask, ScopeAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      ScopeTask& task,
      ScopeAgenda& continuations,
      WorkListReusability& reuse)
  {
    instr->setScope(task.getHeadScope());

    DEBUG_INSTRUCTION_VISITOR
        << "instruction "
        << instr->getRegistrationIndex()
        << " has now scope of enterblock "
        << task.getHeadRegistrationIndex()
        << std::endl;

    InstructionHandler<_Type, ScopeTask, ScopeAgenda>
        ::operator ()(instr,task,continuations,reuse);
  }
};


template <>
struct ScopeInstructionHandler<TEnterBlock>
    : public InstructionHandler<TEnterBlock, ScopeTask, ScopeAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      ScopeTask& task,
      ScopeAgenda& continuations,
      WorkListReusability& reuse)
  {
    EnterBlockInstruction* enter = instruction_cast<TEnterBlock>(instr);
    task.pushScope(enter->scope(), enter->getRegistrationIndex());
    instr->setScope(enter->scope());

    DEBUG_INSTRUCTION_VISITOR
        << "new top with enterblock "
        << enter->getRegistrationIndex()
        << std::endl;

    base_handler::operator ()(instr,task,continuations,reuse);
  }
};

template <>
struct ScopeInstructionHandler<TExitBlock>
    : public InstructionHandler<TExitBlock, ScopeTask, ScopeAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      ScopeTask& task,
      ScopeAgenda& continuations,
      WorkListReusability& reuse)
  {
    task.popScope();

    DEBUG_INSTRUCTION_VISITOR
        << "remove top old enterblock "
        << task.getHeadRegistrationIndex()
        << std::endl;

    base_handler::operator ()(instr,task,continuations,reuse);
  }
};

} // compilation

#endif // _COMPILATION_SCOPE_TASK_H_
